/************************************************************************
 * @file AbstractCommand.h
 * @author Ian Chen
 * @version 1.0
 *
 * @section LICENSE
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 3 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
 * General Public License for more details at
 * http://www.gnu.org/licenses/lgpl.html
 *
 ************************************************************************/
#ifndef ABSTRACTCOMMAND_H
#define ABSTRACTCOMMAND_H

#include <map>
#include <list>
#include <memory>

#include <QMetaType>
#include <QString>
#include <QVariant>

#include <plugin/plugin_global.h>

using namespace std;

typedef map<QString, QMetaType::Type>          CommandParameterTypes;
typedef map<QString, QVariant>                 CommandParameters;
typedef map<QString, QVariant>::iterator       CommandParameterIterator;
typedef map<QString, QVariant>::const_iterator ConstCommandParameterIterator;
typedef pair<QString, QVariant>                CommandParameterPair;

#define SET_COMMAND_PARAM_SETTING(a, b) this->m_oRequiredInputParameters.push_back(pair<QString, QMetaType::Type>(a, b))

namespace antu
{
namespace plugin
{
/**
 * @brief The AbstractCommand class uses command pattern to
 *        handle a particular part of service provided by
 *        AbstractService object.
 *
 * Plugins should provide services by aggregating commands
 * into a service object.
 */
class PLUGINSHARED_EXPORT AbstractCommand
{
public:
    /**
     * @brief The command return type that indicates whether
     *        a command is executed successfully.
     */
    enum CommandReturnType
    {
        CommandLineUnrecognized = -500,
        PluginPaused = -400,
        FrameworkNotSet = -300,
        CommandNotFound = -200,
        ParametersValidationFailure = -100,
        ServiceObjectNotFound = -150,
        ServiceMethodNotFound = -80,
        PluginNotFound = -50,
        AuthorizationFailure = -10,
        ExecutionFailure = -5,
        Success = 0
    };

public:
    /**
     * @brief Default constructor of command object.
     */
    AbstractCommand();

    /**
     * @brief Constructs a command object by name
     * @param commandName Name of the command object.
     */
    AbstractCommand(const QString& commandName)
        : m_sCommandName(commandName)
    {
    }

    /**
     * @brief Returns usage of the command, such as command parameters,
     *        parameter types, etc.
     * @return Usage string of a command object. The usage string is
     *         constructed by values in m_oRequiredInputParameters. Concrete
     *         sub-classes should populate m_oRequiredInputParameters.
     */
    QString usage();

    /**
     * @brief Name of the command object
     * @return
     */
    inline QString commandName() const
    {
        return this->m_sCommandName;
    }

    /**
     * @brief Executes a command.
     * @param context Context of the command.
     * @param inputs Inputs to the command.
     * @param outputs Outputs to the command.
     * @return Indication of command execution success or failure.
     *
     * The execute method relies on m_oRequiredInputParameters fields to
     * verify the validity of the input parameters. Therefore m_oRequiredInputParameters
     * must be populated correctly first before the command can be executed.
     */
    CommandReturnType execute(CommandParameters& context, const CommandParameters& inputs, CommandParameters& outputs);

    /**
     * @brief Parses inputs from input string
     * @param inputStrings Input string in JSON format.
     * @param inputs Parameters parsed from input string.
     * @return Indicates whether parse was successful.
     */
    bool parseInputs(const QString& inputStrings, CommandParameters& inputs);

    /**
     * @brief Converts command parameters (input and output) into JSON format
     * @param params Input or output parameters
     * @return String representation of the parameters in JSON format.
     */
    static QString toJson(const CommandParameters& params);

protected:
    list<pair<QString, QMetaType::Type> > m_oRequiredInputParameters;
    QString m_sCommandName;

    /**
     * @brief Validates the inputs against settings in m_oRequiredInputParameters
     * @param inputs Input parameters.
     * @return Returns true if the input is valid, false otherwise.
     */
    bool isInputValid(const CommandParameters& inputs) const;

    /**
     * @brief Execution logic for concrete sub-classes
     * @param context Command context object.
     * @param inputs Input to the command.
     * @param outputs Output of the command
     * @return Command return type.
     *
     * Sub-classes should implement this method for command business logic.
     */
    virtual CommandReturnType executeCommand(CommandParameters& context, const CommandParameters& inputs, CommandParameters& outputs) = 0;
};
}
}

typedef std::shared_ptr<antu::plugin::AbstractCommand> AbstractCommandSharedPtr;


#endif // ABSTRACTCOMMAND_H
